package gobelinmaker.client;

import com.esotericsoftware.kryonet.Connection;
import com.esotericsoftware.kryonet.Listener;
import com.sun.scenario.scenegraph.JSGPanel;
import com.sun.scenario.scenegraph.SGGroup;
import com.sun.scenario.scenegraph.SGNode;
import com.sun.scenario.scenegraph.SGTransform;
import com.sun.scenario.scenegraph.event.SGMouseAdapter;
import gobelinmaker.server.DiscoveredHoleRequest;
import gobelinmaker.server.DiscoveredHoleResponse;
import gobelinmaker.server.MotorControlRequest;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.util.Arrays;
import javax.swing.DefaultListModel;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;

/**
 * Letapogatott kanafa megjelenítésének tárolója.
 *
 * @author imruf84
 */
public class DiscoveredHolesPanel extends javax.swing.JPanel {

    /**
     * Lyukak száma.
     */
    private long holesCount = 0;
    /**
     * Szerver.
     */
    private final ServerPanel sp;
    /**
     * Rajzterület.
     */
    private final JSGPanel scenePanel = new JSGPanel();
    /**
     * Nagyítási faktor.
     */
    private final Point2D scale = new Point2D.Double(1, 0);
    /**
     * Eltolás mértéke.
     */
    private final Point2D translate = new Point2D.Double();
    /**
     * Kanava transzformációja.
     */
    private final SGTransform.Affine transformation = SGTransform.createAffine(null, null);
    /**
     * Nagyító csúszkája.
     */
    private DoubleJSlider zoomSlider = new DoubleJSlider(1, 10, 1, 1);
    /**
     * Jelenet tárolója.
     */
    private final SGGroup scene = new SGGroup();
    /**
     * Lyukak tárolója.
     */
    private SGGroup holesNode = new SGGroup();
    /**
     * Tű komponense.
     */
    private NeedleComponent needleComponent = new NeedleComponent();

    /**
     * Konstruktor.
     *
     * @param sp szerver
     */
    public DiscoveredHolesPanel(final ServerPanel sp) {

        this.sp = sp;

        final DiscoveredHolesPanel hp = this;

        initComponents();

        // Adatok érkezésének kezelése.
        Connection c = sp.getConnection();
        c.addListener(new Listener() {
            @Override
            public void received(Connection connection, Object o) {
                // Lyukkal kapcsolatos adat érkezett.
                if (o instanceof DiscoveredHoleResponse) {
                    DiscoveredHoleResponse dhr = (DiscoveredHoleResponse) o;

                    // Ha nincs ilyen lyuk, vagy nem a megfelelőt kaptuk meg akkor minden adat megérkezett már.
                    if ((dhr.holeIndex < 0) || (dhr.holeIndex != holesCount)) {
                        receivingDataProgressBar.setVisible(false);
                        getDataButton.setVisible(true);
                        return;
                    }

                    // Komponens létrehozása.
                    DiscoveredHoleComponent dhc = new DiscoveredHoleComponent(dhr.holeData);

                    // Események beállítása.
                    dhc.addMouseListener(new SGMouseAdapter() {
                        @Override
                        public void mouseClicked(MouseEvent e, SGNode node) {
                            if (node instanceof DiscoveredHoleComponent) {
                                DiscoveredHoleComponent dhc = (DiscoveredHoleComponent) node;

                                // Kérdés a tényleges mozgatás előtt.
                                Object[] oo = {"Igen", "Nem"};
                                int n = JOptionPane.showOptionDialog(
                                        hp,
                                        "Valóban szeretné a tűt a(z) " + Arrays.toString(dhc.getHoleData().holePosition) + " koordinátályú lyukra irányítani?",
                                        "Tű navigálása",
                                        JOptionPane.YES_NO_OPTION,
                                        JOptionPane.WARNING_MESSAGE,
                                        null, oo, oo[1]);

                                // Igen esetén megkezdjük a folyamatot.
                                if (JOptionPane.YES_OPTION == n) {
                                    MotorControlRequest mcr = new MotorControlRequest();
                                    mcr.steps.x = (int) (dhc.getHoleData().motorPosition[0] - needleComponent.getHoleData().motorPosition[0]);
                                    mcr.steps.y = (int) (dhc.getHoleData().motorPosition[1] - needleComponent.getHoleData().motorPosition[1]);

                                    sp.getConnection().sendTCP(mcr);
                                }
                            }
                        }
                    });

                    // Letapogatott lyuk tárolása.
                    ((DefaultListModel) holesList.getModel()).addElement(dhc);
                    holesList.setSelectedValue(dhc, true);

                    holesNode.add(dhc);
                    holesCount++;

                    // Soron következő lyuk lekérdezése.
                    getNextHoleFromServer();
                }
            }
        });

        receivingDataProgressBar.setVisible(false);

        zoomSlider.addChangeListener(new javax.swing.event.ChangeListener() {
            @Override
            public void stateChanged(javax.swing.event.ChangeEvent evt) {
                scale.setLocation(zoomSlider.getDoubleValue(), 0);
                updateTransformation();
            }
        });
        jPanel2.add(zoomSlider);

        // Rajzterület létrehozása.
        scenePanel.setPreferredSize(new Dimension(400, 400));
        this.sceneContainer.add(scenePanel, BorderLayout.CENTER);

        // Jelenet.
        this.scenePanel.setScene(this.transformation);
        this.transformation.setChild(this.scene);
        SGGroup g = new SGGroup();
        g.add(this.holesNode);
        this.scene.add(g);

        // Tű hozzáadása.
        this.scene.add(needleComponent);

        // Nagyítás beállítása.
        this.scale.setLocation(this.zoomSlider.getDoubleValue(), 0);

        // Események.
        scenePanel.addComponentListener(new java.awt.event.ComponentAdapter() {
            @Override
            public void componentResized(java.awt.event.ComponentEvent evt) {
                updateTransformation();
            }
        });

        final Point2D dragFrom = new Point2D.Double();
        scenePanel.addMouseListener(new java.awt.event.MouseAdapter() {
            @Override
            public void mousePressed(java.awt.event.MouseEvent evt) {
                if (SwingUtilities.isMiddleMouseButton(evt)) {
                    scene.globalToLocal(evt.getPoint(), dragFrom);
                }
            }
        });
        scenePanel.addMouseMotionListener(
                new java.awt.event.MouseMotionAdapter() {
                    @Override
                    public void mouseDragged(java.awt.event.MouseEvent evt) {

                        // Középső gomb kezelése.
                        if (SwingUtilities.isMiddleMouseButton(evt)) {
                            Point2D dragTo = new Point2D.Double();
                            scene.globalToLocal(evt.getPoint(), dragTo);
                            Point2D dt = new Point2D.Double(dragTo.getX() - dragFrom.getX(), dragTo.getY() - dragFrom.getY());
                            translate.setLocation(translate.getX() + dt.getX(), translate.getY() + dt.getY());
                            updateTransformation();
                            scene.globalToLocal(evt.getPoint(), dragFrom);
                        }
                    }
                });
        scenePanel.addMouseWheelListener(
                new java.awt.event.MouseWheelListener() {
                    @Override
                    public void mouseWheelMoved(java.awt.event.MouseWheelEvent evt) {
                        if (evt.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL) {
                            double newScale = Math.max(zoomSlider.getDoubleMinimum(), Math.min(zoomSlider.getDoubleMaximum(), scale.getX() - (double) evt.getWheelRotation()));
                            zoomSlider.setDoubleValue(newScale);
                        }
                    }
                });

        // Jelenet törlése.
        clearScene();

    }

    /**
     * Transzformációs mátrix frissítése.
     */
    private void updateTransformation() {
        AffineTransform at = new AffineTransform();
        at.translate(this.scenePanel.getWidth() / 2, this.scenePanel.getHeight() / 2);
        at.scale(this.scale.getX(), this.scale.getX());
        at.translate(this.translate.getX(), this.translate.getY());
        this.transformation.setAffine(at);
    }

    /**
     * Soron következő lyuk lekérdezése.
     */
    private void getNextHoleFromServer() {
        DiscoveredHoleRequest dpr = new DiscoveredHoleRequest();
        dpr.holeIndex = holesCount;
        sp.getConnection().sendTCP(dpr);
    }

    /**
     * Lyukak törlése.
     */
    public void clearHoles() {
        SGGroup g = (SGGroup) holesNode.getParent();
        g.remove(holesNode);
        holesNode = new SGGroup();
        g.add(holesNode);
        holesCount = 0;

        ((DefaultListModel) holesList.getModel()).clear();

        // A tűt mindenképpen hozzáadjuk a listához.
        ((DefaultListModel) holesList.getModel()).addElement(needleComponent);
    }

    /**
     * Jelenet törlése.
     */
    private void clearScene() {
        clearHoles();
    }

    /**
     * Tű komponens lekérdezése.
     *
     * @return tű komponens
     */
    public NeedleComponent getNeedleComponent() {
        return this.needleComponent;
    }

    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        informationPanel = new javax.swing.JPanel();
        jPanel2 = new javax.swing.JPanel();
        jLabel1 = new javax.swing.JLabel();
        jPanel1 = new javax.swing.JPanel();
        getDataButton = new javax.swing.JButton();
        jLabel2 = new javax.swing.JLabel();
        receivingDataProgressBar = new javax.swing.JProgressBar();
        jSplitPane1 = new javax.swing.JSplitPane();
        sceneContainer = new javax.swing.JPanel();
        jScrollPane2 = new javax.swing.JScrollPane();
        holesList = new javax.swing.JList();

        setPreferredSize(new java.awt.Dimension(315, 400));
        setLayout(new java.awt.BorderLayout());

        informationPanel.setLayout(new java.awt.BorderLayout());

        jPanel2.setLayout(new javax.swing.BoxLayout(jPanel2, javax.swing.BoxLayout.LINE_AXIS));

        jLabel1.setText("Nagyítás: ");
        jPanel2.add(jLabel1);

        informationPanel.add(jPanel2, java.awt.BorderLayout.CENTER);

        jPanel1.setLayout(new javax.swing.BoxLayout(jPanel1, javax.swing.BoxLayout.LINE_AXIS));

        getDataButton.setText("Adatok letöltése");
        getDataButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                getDataButtonActionPerformed(evt);
            }
        });
        jPanel1.add(getDataButton);

        jLabel2.setText(" ");
        jPanel1.add(jLabel2);

        receivingDataProgressBar.setIndeterminate(true);
        jPanel1.add(receivingDataProgressBar);

        informationPanel.add(jPanel1, java.awt.BorderLayout.EAST);

        add(informationPanel, java.awt.BorderLayout.PAGE_END);

        jSplitPane1.setResizeWeight(1.0);
        jSplitPane1.setPreferredSize(new java.awt.Dimension(270, 134));

        sceneContainer.setBackground(new java.awt.Color(255, 255, 255));
        sceneContainer.setBorder(javax.swing.BorderFactory.createBevelBorder(javax.swing.border.BevelBorder.LOWERED));
        sceneContainer.setMinimumSize(new java.awt.Dimension(300, 300));
        sceneContainer.setPreferredSize(new java.awt.Dimension(300, 300));
        sceneContainer.setLayout(new java.awt.BorderLayout());
        jSplitPane1.setLeftComponent(sceneContainer);

        jScrollPane2.setMinimumSize(new java.awt.Dimension(150, 24));

        holesList.setModel(new DefaultListModel()
        );
        holesList.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                holesListMouseClicked(evt);
            }
        });
        jScrollPane2.setViewportView(holesList);

        jSplitPane1.setRightComponent(jScrollPane2);

        add(jSplitPane1, java.awt.BorderLayout.CENTER);
    }// </editor-fold>//GEN-END:initComponents

    private void getDataButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_getDataButtonActionPerformed

        // Jelenet törlése.
        clearScene();

        getDataButton.setVisible(false);
        receivingDataProgressBar.setVisible(true);

        // Adatkérés igényének az elküldése.
        getNextHoleFromServer();
    }//GEN-LAST:event_getDataButtonActionPerformed

    private void holesListMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_holesListMouseClicked
        if (2 == evt.getClickCount()) {
            DiscoveredHoleComponent dhc = (DiscoveredHoleComponent) holesList.getSelectedValue();

            translate.setLocation(-dhc.getHoleData().motorPosition[0], -dhc.getHoleData().motorPosition[1]);
            updateTransformation();
        }
    }//GEN-LAST:event_holesListMouseClicked


    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton getDataButton;
    private javax.swing.JList holesList;
    private javax.swing.JPanel informationPanel;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JPanel jPanel1;
    private javax.swing.JPanel jPanel2;
    private javax.swing.JScrollPane jScrollPane2;
    private javax.swing.JSplitPane jSplitPane1;
    private javax.swing.JProgressBar receivingDataProgressBar;
    private javax.swing.JPanel sceneContainer;
    // End of variables declaration//GEN-END:variables
}
